1. 概念

  1. Spring的DAO异常体系建立在运行期异常的基础上,封装了源异常

  2. JDBC数据访问流程:

    • 准备资源

    • 启动事务

    • 在事务中执行具体数据访问操作

    • 提交/回滚事务

    • 关闭资源,处理异常

  3. Spring将相同的数据访问流程固化到模板类中,把数据访问中固定和变化的部分分开,同时保证模板类是线程安全的。Spring为不同的持久化技术都提供了简化操作的模板和回调

  4. 数据库事务:原子性,一致性,隔离性和持久性(ACID)

  5. 5类数据库并发问题:

    • 脏读:A事务读取到B事务尚未提交的数据

    • 不可重复读:A事务中读取到B事务已经提交的==更新==数据,即连续两次读取结果不同

    • 幻读:A事务读取B事务的==新增==数据

    • 第一类更新丢失:A事务撤销时覆盖了B事务的提交

    • 第二类更新丢失:A事务覆盖B事务已经提交的数据

  6. JDBC默认情况下自动提交,即每条执行的SQL语句都对应一个事务,AutoCommit = TRUE

  7. Spring基于ThreadLocal解决有状态的Connetion的并发问题,事务同步管理器org.springframework.transaction.support.TransactionSynchronizationManager使用ThreadLocal为不同事务线程提供独立的资源副本

  8. Spring事务管理基于3个接口:TransactionDefinitionTransactionStatusPlatformTransactionManager

  9. Spring为不同持久化技术提供了从TransactionSynchronizationManager获取对应线程绑定资源的工具类,如DataSourceUtils.getConnection(DataSource dataSource)。模板类在内部通过工具类访问TransactionSynchronizationManager中的线程绑定资源

  10. Spring通过事务传播行为控制当前的事务如何传播到被嵌套调用的目标服务接口方法中

  11. 使用<tx:annotation-driven transaction-manager="txManager">对标注@Transactional注解的bean进行加工处理,织入事务管理切面

  12. @Transactional注解的属性

    • 事务传播行为:propagation,默认PROPAGATION_REQUIRED,即如果当前没有事务,就新建一个事务;否则加入到当前事务

    • 事务隔离级别:isolation,默认ISOLATION_DEFAULT

    • 读写事务属性:readOnly

    • 超时时间:timeout

    • 回滚设置:rollbackForrollbackForClassNamenoRollbackFornoRollbackForClassName

  13. 在相同线程中进行相互嵌套调用的事务方法工作于相同的事务中;如果在不同线程中,则工作在独立的事务中

  14. 特殊方法:

    • 注解不能被继承,所以业务接口中的@Transactional注解不会被业务实现类继承;方法处的注解会覆盖类定义处的注解

    • 对于基于接口动态代理的AOP事务,由于接口方法都是public的,实现类的实现方法必须是public的,同时不能使用static修饰符。因此,可以通过接口动态代理实施AOP增强、实现Spring事务的方法只能是publicpublic final

    • 基于CGLib动态代理实施AOP的时候,由于使用finalstaticprivate的方法不能被子类覆盖,相应的,这些方法不能实施AOP增强,实现事务

    • 不能被Spring进行AOP事务增强的方法不能启动事务,但是外层方法的事务上下文仍然可以传播到这些方法中

2. Spring中使用JDBC编程示例

  • 本地mysql建表

CREATE TABLE `t_user` (
  `user_id` varchar(256) NOT NULL,
  `user_name` varchar(256) DEFAULT NULL,
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
  • springDAO.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd ">

    <context:component-scan base-package="com.dao" />

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close"
        p:driverClassName="com.mysql.jdbc.Driver"
        p:url="jdbc:mysql://localhost:3306/sampledb"
        p:username="root"
        p:password="123123" />

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
        p:dataSource-ref="dataSource" />

    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
        p:dataSource-ref="dataSource" />
</beans>
  • User

package com.data;

public class User {

    private String userId;

    private String userName;

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

}
  • BaseDAO

package com.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;

public class BaseDAO {

    @Autowired
    protected JdbcTemplate jdbcTemplate;
}
  • UserDAO

package com.dao;

import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.data.User;
import com.mapper.UserRowMapper;

@Repository
public class UserDAO extends BaseDAO {

    private static final String SQL_GET_USER = "select * from t_user where " + "user_id = ?;";

    private static final String SQL_INSERT_USER = "insert into t_user values(?, ?);";

    private static final String SQL_CLEAN_USER = "delete from t_user where 1=1;";

    @Transactional
    public User getUserById(String userId) {
    return jdbcTemplate.queryForObject(SQL_GET_USER, new Object[]{userId}, new UserRowMapper());
    }

    @Transactional
    public int insertUser(User user) {
        return jdbcTemplate.update(SQL_INSERT_USER, user.getUserId(), user.getUserName());
    }

    @Transactional
    public int cleanUser() {
        return jdbcTemplate.update(SQL_CLEAN_USER);
    }
}
  • UserRowMapper

package com.dao;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.jdbc.core.RowMapper;

import com.data.User;

public class UserRowMapper implements RowMapper<User>{

    public User mapRow(ResultSet rs, int rowNumber) throws SQLException {
        User user = new User();
        user.setUserId(rs.getString("user_id"));
        user.setUserName(rs.getString("user_name"));
        return user;
    }
}
  • BaseTestCase

package com;

import org.junit.Assert;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"/springDAO.xml"})
public class BaseTestCase extends Assert {

}
  • TestUserDAO

package com.dao;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

import com.BaseTestCase;
import com.data.User;

public class TestUserDAO extends BaseTestCase{

    @Before
    @After
    public void clean() {
        dao.cleanUser();
    }

    @Autowired
    private UserDAO dao;

    @Test
    public void getUserById() {
        User user = new User();
        String id = "id";
        String name = "name";
        user.setUserId(id);
        user.setUserName(name);
        assertEquals(dao.insertUser(user), 1);

        user = dao.getUserById(id);
        assertEquals(user.getUserId(), id);
        assertEquals(user.getUserName(), name);
    }
}

天歌
39 声望6 粉丝

天歌应春籥,非是为春风。